package com.neulogistics.contrib.warehouse.api.application.command.handler;

import com.blackto.lib.mediat.unicast.IRequestHandler;
import com.neulogistics.contrib.warehouse.api.application.command.TransferInboundCommand;
import com.neulogistics.contrib.warehouse.api.application.command.TransferOutboundCommand;
import com.neulogistics.contrib.warehouse.api.application.integrationevent.WarehouseIntegrationEventService;
import com.neulogistics.contrib.warehouse.api.application.integrationevent.publish.TransferInboundIntegrationEvent;
import com.neulogistics.contrib.warehouse.api.application.integrationevent.publish.TransferOutboundIntegrationEvent;
import com.neulogistics.contrib.warehouse.api.application.rpc.IWarehouseRPC;
import com.neulogistics.contrib.warehouse.api.service.ThreadLocalService;
import com.neulogistics.contrib.warehouse.domain.model.aggregate.CirculationOrder;
import com.neulogistics.contrib.warehouse.domain.model.aggregate.Inventory;
import com.neulogistics.contrib.warehouse.domain.model.enumeration.CirculationDetail;
import com.neulogistics.contrib.warehouse.domain.model.enumeration.CirculationStatus;
import com.neulogistics.contrib.warehouse.domain.model.enumeration.CirculationType;
import com.neulogistics.contrib.warehouse.domain.model.valueobject.GoodsItem;
import com.neulogistics.contrib.warehouse.domain.repository.ICirculationOrderRepository;
import com.neulogistics.contrib.warehouse.domain.repository.IInventoryRepository;
import com.neulogistics.infrastructure.cache.idservice.RedisIdUtil;
import com.neulogistics.infrastructure.common.annotation.ProduceEvent;
import com.neulogistics.infrastructure.integrationeventlog.model.IntegrationEventConstant;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;

@Component
public class TransferOutboundCommandHandler implements IRequestHandler<TransferOutboundCommand, Boolean> {
    @Resource
    ICirculationOrderRepository circulationOrderRepository;

    @Resource
    IInventoryRepository inventoryRepository;

    @Resource
    private RedisIdUtil redisIdUtil;

    @Resource
    RedissonClient redissonClient;

    @Resource
    IWarehouseRPC warehouseRPC;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private WarehouseIntegrationEventService warehouseIntegrationEventService;

    private final String Lock_Name_Prefix;

    public TransferOutboundCommandHandler(@Value("${spring.application.name}") String appName) {
        Lock_Name_Prefix = "lock:" + appName +":Inventory:";
    }

    @Override
    @ProduceEvent
    public Boolean handle(TransferOutboundCommand command) throws Exception {
        // 根据出库单id查询对应实体
        CirculationOrder circulationOrder = circulationOrderRepository.findById(command.circulationId);

        // 上锁！
        String lockName = Lock_Name_Prefix + command.warehouseId + ":" + circulationOrder.getGoodsItems().get(0).getGoodsId();
        RLock lock = redissonClient.getLock(lockName);
        lock.lock();

        try {
            // 查询对应库存
            GoodsItem goodsItem = circulationOrder.getGoodsItems().get(0);
            Inventory inventory = inventoryRepository.findByWarehouseAndGoods(
                    command.warehouseId, goodsItem.getGoodsId());

            // 调拨出库：减库存
            inventory.transferOutbound(goodsItem.getCount());

            // 出库单状态置为已完成
            circulationOrder.setCirculationStatus(CirculationStatus.Completed);

            // 生成调拨出库事件，订单状态更改为运输中
            Long orderId = warehouseRPC.getOrderIdByTransfer(circulationOrder.getTransferId());
            Long eventId = redisIdUtil.nextId(IntegrationEventConstant.Integration_Event_Id_Key);
            TransferOutboundIntegrationEvent transferOutboundIntegrationEvent = new TransferOutboundIntegrationEvent(eventId);
            transferOutboundIntegrationEvent.setOrderId(orderId);

            // 持久化
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    inventoryRepository.save(inventory);

                    circulationOrderRepository.save(circulationOrder);

                    warehouseIntegrationEventService.addAndSaveEvent(transferOutboundIntegrationEvent, ThreadLocalService.transactionId.get());
                } catch (Exception e){
                    // 触发回滚
                    throw new RuntimeException(e.getCause().getMessage());
                }
                }
            });
        } finally {
            lock.unlock();
        }

        return Boolean.TRUE;
    }
}
